Files for my website
bwc9876.dev
1---
2import { type CollectionEntry, getCollection } from "astro:content";
3import { Image } from "astro:assets";
4import Layout from "@layouts/Layout.astro";
5import IconLink from "@components/IconLink.astro";
6import { Icon } from "astro-icon/components";
7export async function getStaticPaths() {
8 const renameEntries = await getCollection("projects");
9 return renameEntries.map((entry) => ({
10 params: { slug: entry.slug },
11 props: { entry }
12 }));
13}
14const { entry } = Astro.props as { entry: CollectionEntry<"projects"> };
15const { Content } = await entry.render();
16
17const firstOtherLink = entry.data.links?.other?.[0];
18
19const restOther = entry.data.links?.other?.slice(1);
20
21const og = {
22 src: entry.data.image,
23 alt: entry.data.name
24};
25---
26
27<Layout title={entry.data.name} description={entry.data.summary} keywords={entry.data.tags} og={og}>
28 <div class="hero">
29 <div class="txt">
30 <div class="project-header">
31 <h1 class="gradient-text">{entry.data.name}</h1>
32 <p>
33 {entry.data.timespan.from}{entry.data.timespan.to && <> - {entry.data.timespan.to}</>} •
34 {entry.data.tags.join(" • ")}
35 </p>
36 </div>
37 <p>{entry.data.summary}</p>
38 <div class="ctas">
39 {
40 entry.data.links?.github && (
41 <span>
42 <a href={`https://github.com/${entry.data.links.github}`} role="button">
43 <Icon name="bi:github" /> GitHub
44 </a>
45 </span>
46 )
47 }
48 {
49 firstOtherLink && (
50 <span>
51 <a href={firstOtherLink.url} role="button" class="secondary">
52 <Icon name={`${firstOtherLink.iconPackOverride ?? "bi"}:${firstOtherLink.icon}`} />
53 {firstOtherLink.label}
54 </a>
55 </span>
56 )
57 }
58 </div>
59 <div class="links">
60 {
61 restOther &&
62 restOther.map((l) => (
63 <span>
64 <IconLink
65 icon={l.icon}
66 overridePack={l.iconPackOverride}
67 href={l.url}
68 placement="bottom"
69 label={l.label}
70 />
71 </span>
72 ))
73 }
74 </div>
75 </div>
76 <div class="img-container">
77 <Image
78 transition:name={`project-img-${entry.slug}`}
79 format="webp"
80 width={474}
81 height={474}
82 alt={entry.data.name}
83 src={entry.data.image}
84 />
85 </div>
86 </div>
87 <h2>About This Project</h2>
88 <Content />
89</Layout>
90
91<style>
92 div.project-header h1 {
93 margin-bottom: var(--1);
94 }
95
96 div.project-header p {
97 font-size: var(--2);
98 margin: 0;
99 }
100
101 .hero {
102 display: flex;
103 gap: var(--5);
104 justify-content: space-between;
105 align-items: center;
106 margin: var(--4) 0;
107 }
108
109 @media (width <= 1300px) {
110 .hero {
111 flex-direction: column-reverse;
112 margin-top: var(--1);
113 gap: var(--2);
114 text-align: center;
115 }
116
117 .links,
118 .ctas {
119 margin: auto;
120 }
121
122 .hero p {
123 margin: 0;
124 }
125
126 div.project-header h1 {
127 margin-top: 0;
128 }
129 }
130
131 .hero .txt {
132 width: 100%;
133 flex-grow: 1;
134 display: flex;
135 flex-direction: column;
136 gap: var(--2);
137 }
138
139 .hero .ctas {
140 display: flex;
141 align-items: center;
142 gap: var(--2);
143 }
144
145 .hero .ctas a[role="button"] {
146 display: flex;
147 align-items: center;
148 gap: var(--small);
149 }
150
151 .hero .img-container {
152 flex-grow: 1;
153 width: 100%;
154 justify-content: end;
155 align-items: center;
156 display: flex;
157 margin: auto;
158 }
159
160 .hero .img-container img {
161 border-radius: 5px;
162 max-width: 100%;
163 height: auto;
164 margin: auto;
165 }
166
167 .links {
168 display: flex;
169 gap: var(--2);
170 }
171</style>